package com.alex.seo.dao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.Set;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Required;

public abstract class AbstractHibernateDaoImpl<Type extends Identifieble<TypeId>, TypeId extends Serializable> implements AbstractDao<Type, TypeId> {

	protected SessionFactory sessionFactory;
	
	private final Class<Type> typeClass;
	
	private final String selectAllQuery;
	
	@SuppressWarnings("unchecked")
	public AbstractHibernateDaoImpl() {
		Object clazz = getClass().getGenericSuperclass();
		while (!(clazz instanceof ParameterizedType)) {
			clazz = ((Class<?>) clazz).getGenericSuperclass();
		}
		ParameterizedType parameterizedType = (ParameterizedType) clazz;
		this.typeClass = (Class<Type>) parameterizedType.getActualTypeArguments()[0];
		
		this.selectAllQuery = "select t from " + typeClass.getSimpleName() + " t where id in :ids";
	}

	@Required
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}
	
	@Override
	public void flush() {
		sessionFactory.getCurrentSession().flush();
	}
	
	@Override
	public void rollback() {
		sessionFactory.getCurrentSession().getTransaction().rollback();
	}

	public void persist(Type entity) {
		sessionFactory.getCurrentSession().persist(entity);
	}
	
	public void delete(Type entity) {
		sessionFactory.getCurrentSession().delete(entity);
	}
	
	@SuppressWarnings("unchecked")
	public Type get(TypeId id, boolean ignoreMissing) {
		Type result = (Type) sessionFactory.getCurrentSession().get(typeClass, id);
		
		if (!ignoreMissing && result == null) {
			throw new ObjectNotFoundException("Object not found. Type=" + typeClass + ", id=" + id);
		}
		
		return result;
	}
	
	@Override
	@SuppressWarnings("unchecked")
	public List<Type> getAll(Set<TypeId> ids, boolean ignoreMissing) {
		List<Type> result = sessionFactory.getCurrentSession()
				.createQuery(selectAllQuery)
				.setParameterList("ids", ids)
				.list();
		
		if (!ignoreMissing && result.size() != ids.size()) {
			throw new ObjectNotFoundException("Requested " + ids.size() + " " + typeClass + " objects, found " + result.size());
		}
		
		return result;
	}
}
